'''工具模块，包含类
1.状态类的基类
2.主控类Control
工具方法
1.获取图片get_image
2.加载所有图形load_all_gfx
一个键映射，左右下跳跃攻击'''

import os
import pygame as pg
from abc import abstractmethod
from . import constants

# Dictionary defining keybindings for different actions
keybinding = {'action': pg.K_s, 'jump': pg.K_SPACE, 'left': pg.K_LEFT, 'right': pg.K_RIGHT, 'down': pg.K_DOWN}


class State:
    '''
    Base class for all states. Contains the methods that all states should implement, and the attributes that all states should have.
    所有的子类 ../states/main_menu.py/Menu
    ../states/load_screen.py/LoadScreen
    ../states/load_screen.py/GameOver
    ../states/load_screen.py/TimeOut
    ../states/level1.py/Level1
    核心流程就是：
    1. 获取事件 Control的main方法里self.event_loop()获取输入的键，保存到self.keys中
    2. 更新游戏状态 Control的main方法里self.update()响应游戏退出事件，当前state的done状态（跳转到下一个状态），更新当前时间self.current_time
    3. 绘制游戏画面 Control的main方法里pg.display.update()
    '''

    def __init__(self):
        # State variables
        self.start_time = 0.0  # 开始时间
        self.current_time = 0.0  # 当前时间
        self.done = False  # 当前状态是否完成
        self.next = None  # 下一个状态
        self.persist = {}  # 用于存储游戏状态信息的字典

    @abstractmethod
    def startup(self, current_time, persist):
        '''Abstract method to be overridden in child classes'''

    def cleanup(self):
        # Reset the state after it's done
        self.done = False
        return self.persist

    @abstractmethod
    def update(self, surface, keys, current_time):
        '''Abstract method to be overridden in child classes'''


class Control:
    '''Control类负责管理游戏状态的切换和更新。它是游戏的主循环控制器。'''

    def __init__(self):
        # Control variables
        self.screen = pg.display.get_surface()
        self.done = False
        self.clock = pg.time.Clock()
        self.fps = 60
        self.current_time = 0.0
        self.keys = pg.key.get_pressed()
        self.state_dict = {}
        self.state_name = None
        self.state = None
        self.show_fps = False

    def setup_states(self, state_dict, start_state):
        '''使用字典state_dict和初始状态start_state设置游戏状态'''
        self.state_dict = state_dict
        self.state_name = start_state
        self.state = self.state_dict[self.state_name]

    def update(self):
        # Update current game state
        self.current_time = pg.time.get_ticks()
        if self.state.done:
            self.flip_state()
        self.state.update(self.screen, self.keys, self.current_time)

    def flip_state(self):
        # Switch to the next game state
        self.state_name = self.state.next
        persist = self.state.cleanup()
        self.state = self.state_dict[self.state_name]
        self.state.startup(self.current_time, persist)

    def event_loop(self):
        '''
        事件循环，处理用户输入和窗口事件，get_pressed返回的是所有按键的状态对应的元祖，可以通过按键名称获取是否按键状态。
        如果我按下了一次右键，会触发两次该事件，第一次是KEYDOWN，此时keys[right]=true，第二次是KEYUP,此时keys[right]=false。
        '''
        for event in pg.event.get():
            if event.type == pg.QUIT:
                self.done = True
            elif event.type == pg.KEYDOWN:
                self.keys = pg.key.get_pressed()
                self.toggle_show_fps(event.key)
            elif event.type == pg.KEYUP:
                self.keys = pg.key.get_pressed()
        # 处理是否显示FPS，显示的话显示在窗口标题上
        if self.show_fps:
            fps = self.clock.get_fps()
            with_fps = "{} - {:.2f} FPS".format(constants.ORIGINAL_CAPTION, fps)
            pg.display.set_caption(with_fps)

    def toggle_show_fps(self, key):
        '''切换是否显示FPS在窗口标题上'''
        if key == pg.K_F5:
            self.show_fps = not self.show_fps

    def main(self):
        '''Main game loop'''
        while not self.done:
            self.event_loop()
            self.update()
            pg.display.update()
            self.clock.tick(self.fps)


def get_image(sheet, x, y, width, height, colorkey, scale, use_srcalpha=False):
    '''从精灵表中获取指定位置的图像，并进行缩放和设置透明色。马里奥的图像还需要闪烁表示无敌，需要调用时use_srcalpha=true，不然会直接完全透明'''
    if use_srcalpha:
        image = pg.Surface([width, height], pg.SRCALPHA)
    else:
        image = pg.Surface([width, height])
    rect = image.get_rect()

    image.blit(sheet, (0, 0), (x, y, width, height))
    image.set_colorkey(colorkey)
    image = pg.transform.scale(image, (int(rect.width * scale), int(rect.height * scale)))
    return image


def load_all_gfx(directory, colorkey=(255, 0, 255), accept=('.png', '.jpg', '.bmp', '.gif')):
    '''加载指定目录下的所有图形文件，并返回一个字典，键为文件名（不含扩展名），值为加载的图像对象'''
    graphics = {}
    for pic in os.listdir(directory):
        name, ext = os.path.splitext(pic)
        if ext.lower() in accept:
            img = pg.image.load(os.path.join(directory, pic))
            if img.get_alpha():
                print(f'load {name} get alpha is {img.get_alpha()}')
                img = img.convert_alpha()
            else:
                # 只有title_screen，text_images两个图片进入该分支，需要将颜色(255, 0, 255) ​品红色（Magenta）设为透明
                print(f'load {name} set colorkey {255, 0, 255}, alpha is {img.get_alpha()}')
                img = img.convert()
                img.set_colorkey(colorkey)
            graphics[name] = img
    return graphics
